﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Agr.CQRS.Infrastructure.Commands;
using Agr.CQRS.Infrastructure.DI;
using Agr.CQRS.Infrastructure.Events;
using Agr.CQRS.Infrastructure.Orm;
using Agr.CQRS.ViewModel;
using NHibernate;
using NHibernate.Tool.hbm2ddl;
using Ninject;

namespace Agr.CQRS
{
    public class Bootstrapper
    {
        private ISessionFactory nhSessionFactory;
        private string connectionString;
        private Assembly[] mappingAssemblies;

        public Bootstrapper(string connectionString, params Assembly[] additionalAssembliesWithHibernateMappingFiles)
        {
            this.connectionString = connectionString;
            this.mappingAssemblies = additionalAssembliesWithHibernateMappingFiles;
            nhSessionFactory = OrmContext.CreateSessionFactory(connectionString, mappingAssemblies);
        }

        public void CreateSchema()
        {
#if DEBUG
            var conf = OrmContext.CreateConfiguration(connectionString, mappingAssemblies);
            var schema = new SchemaExport(conf);
            schema.Create(true, true);
            return;
#endif
            throw new InvalidOperationException("Only in Debug mode");
        }

        private List<IKernel> createdKernels = new List<IKernel>();
        private IKernel GetKernel()
        {
            var kernel = new StandardKernel(new RegisterDefaultMappings());
            kernel.Bind<ISessionFactory>().ToConstant(nhSessionFactory);
            createdKernels.Add(kernel);
            return kernel;
        }

        public BootstrappedDomainModel BootstrapDomainModel()
        {
            var kernel = GetKernel();
            var router = kernel.Get<CommandRouter>();
            router.Start();

            return new BootstrappedDomainModel(kernel);
        }

        public BootstrappedViewModelUpdater BootstrapViewModelUpdater(params Type[] updaters)
        {
            var kernel = GetKernel();

            var bootstrapped = new BootstrappedViewModelUpdater();

            foreach (var updaterType in updaters)
                bootstrapped.ViewModelUpdaters.Add(kernel.Get(updaterType) as ViewModelUpdater);

            return bootstrapped;
        }

        public BootstrappedViewModel BootstrapViewModelRepository()
        {
            var kernel = GetKernel();

            return new BootstrappedViewModel(kernel);
        }

        public void Dispose()
        {
            if (nhSessionFactory != null)
                nhSessionFactory.Dispose();
            nhSessionFactory = null;

            foreach (var k in createdKernels)
                k.Dispose();
            createdKernels.Clear();
        }
    }

    public class BootstrappedDomainModel
    {
        internal BootstrappedDomainModel(IKernel kernel)
        {
            CommandIssuer = kernel.Get<CommandIssuer>();
        }
        public CommandIssuer CommandIssuer { get; private set; }
    }
    public class BootstrappedViewModelUpdater
    {
        public IList<ViewModelUpdater> ViewModelUpdaters = new List<ViewModelUpdater>();

        public void Stop()
        {
            foreach (var updater in ViewModelUpdaters)
            {
                updater.Stop();
            }
        }
    }
    public class BootstrappedViewModel
    {
        private IKernel kernel;
        internal BootstrappedViewModel(IKernel kernel)
        {
            this.kernel = kernel;
        }

        public ViewModelQueryFactory CreateViewModelQueryFactory()
        {
            //kernel.Get seems not to be thread safe
            //this lock is however unacceptable in a real-life application
            lock (kernel)
                return kernel.Get<ViewModelQueryFactory>();
        }
    }

}
